package tc.de.krawallmann.extension;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.newdawn.slick.Input;

import tc.de.krawallmann.event.CommandKeyListener;
import tc.de.krawallmann.input.InputMap;
import tc.de.krawallmann.persistence.XMLStorage;

/**
 * The Class KeyboardInputMapExtension manages InputMaps
 *  @author KrawallMann
 * @version 1.0
 */
public class KeyboardInputMapExtension implements KeyboardExtension, PropertyExtension,
		PersistenceExtension {
	
	/** The defaults. */
	private transient final Map<String, Integer> defaults;
	
	/** The input file. */
	private transient final String inputFile;
	
	/** The key listeners. */
	private transient final List<CommandKeyListener> keyListeners =
			new ArrayList<CommandKeyListener>();
	
	/** The map. */
	private transient InputMap map;

	/**
	 * Instantiates a new KeyboardInputMapExtension.
	 * 
	 * @param inputFile
	 *            the input file
	 * @param defaults
	 *            the default key bindings
	 */
	public KeyboardInputMapExtension(final String inputFile, final Map<String, Integer> defaults) {
		super();
		if (inputFile == null) {
			throw new IllegalArgumentException("Input file is null!");
		}
		this.inputFile = inputFile;
		if (defaults == null) {
			throw new IllegalArgumentException("Default settings are null!");
		}
		this.defaults = defaults;
	}

	/**
	 * Adds the CommandKeyListener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean addCommandKeyListener(final CommandKeyListener listener) {
		return keyListeners.add(listener);
	}

	/**
	 * Clear the CommandKeyListeners.
	 */
	public void clearCommandKeyListeners() {
		keyListeners.clear();
	}

	/**
	 * Contains a CommandKeyListener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean containsCommandKeyListener(final CommandKeyListener listener) {
		return keyListeners.contains(listener);
	}

	/**
	 * Gets the key for the given action
	 * 
	 * @param action
	 *            the action
	 * @return the key for the given action
	 */
	public int getKeyFor(final String action) {
		return map.getFirstKeyFor(action);
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.extension.Extension#init()
	 */
	@Override
	public void init() {
		// Empty!
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.ControlledInputReciever#inputEnded()
	 */
	@Override
	public void inputEnded() {
		// Empty!
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.ControlledInputReciever#inputStarted()
	 */
	@Override
	public void inputStarted() {
		// Empty!
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.ControlledInputReciever#isAcceptingInput()
	 */
	@Override
	public boolean isAcceptingInput() {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.KeyListener#keyPressed(int, char)
	 */
	@Override
	public void keyPressed(final int key, final char arg1) {
		if (map.containsKey(key)) {
			for (final String action : map.get(key)) {
				for (final CommandKeyListener listener : keyListeners) {
					listener.commandKeyPressed(action);
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.KeyListener#keyReleased(int, char)
	 */
	@Override
	public void keyReleased(final int key, final char arg1) {
		if (map.containsKey(key)) {
			for (final String action : map.get(key)) {
				for (final CommandKeyListener listener : keyListeners) {
					listener.commandKeyReleased(action);
				}
			}
		}
	}

	/**
	 * Put.
	 * 
	 * @param key
	 *            the key
	 * @param action
	 *            the action
	 */
	public void put(final int key, final String action) {
		if (!map.containsKey(key)) {
			map.put(key, new ArrayList<String>());
		}
		map.get(key).add(action);
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.extension.PersistenceExtension#read()
	 */
	@Override
	public boolean read() {
		try {
			map = XMLStorage.deserialize(map, inputFile);
			return true;
		} catch (final Exception e) {
			map = new InputMap();
			setDefaults();
			return false;
		}
	}

	/**
	 * Removes the.
	 * 
	 * @param key
	 *            the key
	 * @param action
	 *            the action
	 */
	public void remove(final int key, final String action) {
		map.remove(key, action);
	}

	/**
	 * Removes the CommandKeyListener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean removeCommandKeyListener(final CommandKeyListener listener) {
		return keyListeners.remove(listener);
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.extension.PropertyExtension#setDefaults()
	 */
	@Override
	public void setDefaults() {
		map.clear();
		for (final java.util.Map.Entry<String, Integer> entry : defaults.entrySet()) {
			map.put(entry.getValue(), entry.getKey());
		}
	}

	/* (non-Javadoc)
	 * @see org.newdawn.slick.ControlledInputReciever#setInput(org.newdawn.slick.Input)
	 */
	@Override
	public void setInput(final Input input) {
		// Empty!
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.extension.PersistenceExtension#store()
	 */
	@Override
	public boolean store() {
		try {
			XMLStorage.store(map, inputFile);
			return true;
		} catch (final Exception e) {
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.extension.Extension#update(long)
	 */
	@Override
	public void update(final long delta) {
		// Empty!
	}
}
